package com.onLineCar.vehicle.config;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.*;

/**
 * 限流降级策略
 * @author 李新科
 * */
@Component
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RateLimiter {
    @Value("${server.maxRequests}")
    private int maxRequests; // 最大并发请求量
    @Value("${server.maxRequestRate}")
    private int maxRequestRate; // 最大请求速率
    private ThreadPoolExecutor executor; // 请求执行器
    private long lastRequestTime;  // 上次请求的时间戳

    public RateLimiter( int maxRequests, int maxRequestRate) {
        this.maxRequests = maxRequests;
        this.maxRequestRate = maxRequestRate;
        this.executor = new ThreadPoolExecutor(
                maxRequests, // 核心线程数
                maxRequestRate, // 线程池最大容量
                60L, // 非核心线程的闲置超时时间
                TimeUnit.SECONDS, // 超时时间单位
                new LinkedBlockingQueue<>(maxRequests), // 线程等待队列
                new ThreadPoolExecutor.AbortPolicy() // 拒绝策略：直接抛出异常
        );
        this.lastRequestTime = System.currentTimeMillis();
    }

    public void submitRequest(Runnable request) throws RejectedExecutionException {
        synchronized (this) {
            long currentTime = System.currentTimeMillis();
            // 检查当前并发请求量是否超过了maxRequests。
            if (executor.getActiveCount() >= maxRequests) {
                throw new RejectedExecutionException("Max concurrent requests exceeded");
            }

            // 检查最大请求速率是否超过限制
            if (maxRequestRate > 0) {
                long interval = 1000 / maxRequestRate;
                // 检查当前时间与上次请求的时间差是否小于计算得到的时间间隔。
                if (currentTime - lastRequestTime < interval) {
                    throw new RejectedExecutionException("Max request rate exceeded");
                }
                // 将当前时间设置为上次请求的时间戳。
                lastRequestTime = currentTime;
            }
        }
        // 提交请求到执行器处理
        executor.submit(request);
    }
}
